#include <iostream>
#include <unistd.h>
#include <cstdlib>
#include <string>
#include <cstring>
#include <cstdio>
#include <sys/types.h>
#include <sys/wait.h>
using namespace std;

// ======================= pipe接口代码样例 ======================================

// child - 写
// void Writer(int wfd)
// {
//     string s = "hello, I am child";
//     pid_t self = getpid();
//     int number = 0;

//     // 把上述内容打包进一个buffer字符数组里
//     char buffer[1024];
//     while (true)
//     {
//         buffer[0] = 0; // 设置成0表示这是个空字符串（只是为了提醒阅读代码的人，我把这个数组当做字符串了）
//         // snprintf函数
//         snprintf(buffer, sizeof(buffer), "%s-%d-%d", s.c_str(), self, ++number);

//         // 发送/写入给父进程（写入管道文件）
//         write(wfd, buffer, strlen(buffer));
//         sleep(1); // 1秒一次写入到管道中
//     }
// }

// // father - 读
// void Reader(int rfd)
// {
//     char buffer[1024];
//     while (true)
//     {
//         buffer[0] = 0; // 表示buffer是字符串
//         ssize_t n = read(rfd, buffer, sizeof(buffer));
//         if (n > 0) // 读取成功
//         {
//             buffer[n] = 0; // 将字符串最后设置成'\0'
//             cout << "father[" << getpid() << "] get a message" << "]# " << buffer << endl;
//         }
//     }
// }

// int main()
// {
//     // pipefd[0]是管道的读取端
//     // pipefd[1]是管道的写入端
//     int pipefd[2] = {0};

//     // 1. 父进程调用pipe函数创建管道
//     int n = pipe(pipefd);
//     if (n == -1) // 创建管道失败
//     {
//         return 1;
//     }

//     // 2. 父进程fork创建子进程
//     pid_t id = fork();
//     if (id == -1) // 子进程创建失败
//     {
//         return 2;
//     }
//     else if (id == 0) // 子进程
//     {
//         // 3. 子进程执行写，因此要关闭读
//         close(pipefd[0]);
//         Writer(pipefd[1]);
//         close(pipefd[1]);
//         exit(0);
//     }
//     else // 父进程
//     {
//         // 3. 父进程执行读，因此要关闭写
//         close(pipefd[1]);
//         Reader(pipefd[0]);
//         close(pipefd[0]);

//         // 回收子进程
//         pid_t rid = waitpid(id, nullptr, 0);
//         if (rid == -1) // 回收失败
//         {
//             return 3;
//         }
//     }
//     return 0;
// }

// ============================== 匿名管道的四大特征 ================================

// 1. 读写端正常，管道如果为空，读端就会被阻塞，等待写端的数据。
// int main()
// {
//     int pipefd[2] = {0};
//     // 1. 创建匿名管道
//     int n = pipe(pipefd);
//     // 2. 创建子进程
//     pid_t id = fork();
//     if (id == 0) // 子进程
//     {
//         // 写
//         close(pipefd[0]);
//         int cnt = 0;
//         while (true)
//         {
//             char c = 'a';
//             write(pipefd[1], &c, 1);
//             sleep(1); // 1秒向管道写

//             cnt++;
//             if (cnt == 3) // 当向管道写入三次消息后就不再写入
//             {
//                 break;
//             }
//         }
//         sleep(100); // 观察程序现象
//     }
//     else // 父进程
//     {
//         // 读
//         close(pipefd[1]);
//         char buffer[1024];
//         while (true)
//         {
//             ssize_t readRes = read(pipefd[0], buffer, sizeof(buffer));
//             if (readRes != -1)
//             {
//                 buffer[readRes] = 0;
//                 cout << "I am father. I get msg from my child: " << buffer << endl;
//             }
//         }
//     }
//     return 0;
// }

// 2. 读写端正常，管道如果被写满，写端就要被阻塞
// int main()
// {
//     int pipefd[2] = {0};
//     // 1. 创建匿名管道
//     int n = pipe(pipefd);
//     // 2. 创建子进程
//     pid_t id = fork();
//     if (id == 0) // 子进程
//     {
//         // 写
//         close(pipefd[0]);
//         int count = 0;
//         while (true)
//         {
//             char c = 'a';
//             write(pipefd[1], &c, 1);
//             cout << "我是子进程，我已经写了" << ++count << "字节的数据" << endl;
//         }
//     }
//     else // 父进程
//     {
//         close(pipefd[1]);
//         while (true)
//         {
//             ;
//         }
//     }
//     return 0;
// }

// 3. 读端正常读，写端关闭
// int main()
// {
//     int pipefd[2] = {0};
//     // 1. 创建匿名管道
//     int n = pipe(pipefd);
//     // 2. 创建子进程
//     pid_t id = fork();
//     if (id == 0) // 子进程
//     {
//         // 写
//         close(pipefd[0]);
//         int cnt = 3;
//         while (cnt--) // 向管道写完三次后关闭
//         {
//             char c = 'a';
//             write(pipefd[1], &c, 1);
//             sleep(1);
//         }
//     }
//     else // 父进程
//     {
//         // 读
//         close(pipefd[1]);
//         char buffer[1024];
//         while (true)
//         {
//             ssize_t readRes = read(pipefd[0], buffer, sizeof(buffer));
//             if (readRes != -1)
//             {
//                 buffer[readRes] = 0;
//                 cout << "I am father. I get msg from my child: " << buffer
//                      << ". and readRes is " << readRes << endl;
//                 sleep(1);
//             }
//             else if (readRes == 0) // 说明写端关闭，那就没必要再通信了，读端直接退出
//             {
//                 break;
//             }
//         }
//     }
//     return 0;
// }

// 4. 写端正常，读端关闭
void Reader(int pfd)
{
    char buffer[1024];
    int cnt = 0;
    while (true)
    {
        ssize_t readRes = read(pfd, buffer, sizeof(buffer));
        if (readRes != -1)
        {
            buffer[readRes] = 0;
            cout << "I am father. I get msg from my child: " << buffer
                 << ". and readRes is " << readRes << endl;
            ++cnt;
            if (cnt == 3) // 读了三次就不读了，关闭读取端
            {
                close(pfd);
                break;
            }
            sleep(1);
        }
        else if (readRes == 0)
        {
            break;
        }
    }
}

int main()
{
    int pipefd[2] = {0};
    // 1. 创建匿名管道
    int n = pipe(pipefd);
    // 2. 创建子进程
    pid_t id = fork();
    if (id == 0) // 子进程
    {
        // 写
        close(pipefd[0]);
        while (true)
        {
            char c = 'a';
            write(pipefd[1], &c, 1);
            sleep(1);
        }
    }
    else // 父进程
    {
        // 读
        close(pipefd[1]);
        Reader(pipefd[0]);
        close(pipefd[0]);
        // 回收子进程
        int status = 0;
        pid_t rid = waitpid(id, &status, 0);
        if (rid > 0)
        {
            cout << "退出码：" << WEXITSTATUS(status) << ", 退出信号：" << WTERMSIG(status) << endl;
        }
    }
    return 0;
}